﻿using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using TinyEngine.Core.Camera;
using TinyEngine.Core.Entities;
using TinyEngine.Core.Renderer;
using TinyEngine.Core.Scene;

namespace TinyEngine.Core.Services
{
    public class TinyGameEngine : ITinyService
    {
        private GraphicsDevice graphics;
        private ContentManager content;

        public Color ClearColor { get; set; }

        public ContentManager Content
        {
            get { return content; }
        }

        public GraphicsDevice Device
        {
            get { return graphics; }
        }

        public TinyGameEngine(GraphicsDevice gfx, ContentManager content)
        {
            this.graphics = gfx;
            this.content = content;
            ClearColor = Color.Crimson;
        }

        public void Register()
        {
            
        }

        public void UnRegister()
        {
            
        }

        internal void Update(GameTime gameTime)
        {
            var scene = ServiceManager.GetService<SceneManager>().ActiveGameScene;
            float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;

            ServiceManager.GetService<InputService>().Update(elapsed);

            foreach (var cam in scene.Cameras)
            {
                cam.Update(elapsed);
            }

            foreach (var item in scene.Graph.Entities)
            {
                UpdateItem(item, elapsed);
            }

            scene.RegisterNewItems();
            scene.DeleteItems();

            var prim = ServiceManager.GetService<PrimitiveBatchService>();
            if (scene.FirstCamera != null)
            {
                prim.Batch.SetTransform(scene.FirstCamera.Transform);
            }
        }

        private void UpdateItem(IEntity item, float time)
        {
            if (!item.IsRegistered)
                return;

            var update = item as IUpdatable;
            if (update != null)
            {
                update.Update(time);
            }

            if (item.Components.Count > 0)
            {

                foreach (var comp in item.Components)
                {
                    comp.Update(time);
                }
            }

            foreach (var child in item.Children)
            {
                UpdateItem(child, time);
            }

            if (item.MarkForDelete)
            {
                var scene = ServiceManager.GetService<SceneManager>().ActiveGameScene;
                scene.MarkEntityForDelete(item);
            }
        }

        public void DrawScene(GameTime gameTime, TinyScene scene, ICamera camera)
        {
            DrawScene(gameTime, scene, camera, true);
        }

        public void DrawScene(GameTime gameTime, TinyScene scene, ICamera camera, bool rawScene)
        {
        
        
            if (scene == null)
                return;

            var rt = Services.ServiceManager.GetService<RenderTargetService>();
            if (rt.RawScene == null)
                return;
            if (rawScene)
            {
                Device.SetRenderTarget(rt.RawScene);

                Device.Clear(ClearColor);
            }
            var renderer = Services.ServiceManager.GetService<SpriteBatchService>();
            var debugrenderer = Services.ServiceManager.GetService<PrimitiveBatchService>();
            var cam = camera;
            var transform = Matrix.Identity;
            if (cam != null)
                transform = cam.Transform;

            renderer.Batch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend, null, null, null, null, transform);

            foreach (var item in scene.Graph.Entities)
            {
                DrawItemAndChildren(item, (float)gameTime.ElapsedGameTime.TotalSeconds, false, camera);
            }

            renderer.Batch.End();

            if (rawScene)
            {
                Device.SetRenderTarget(null);
            }
            /*ServiceManager.GetService<PrimitiveBatchService>().Batch.SetTransform(camera.Transform);
            
            foreach (var item in scene.Graph.Entities)
            {
                DrawItemAndChildren(item, (float)gameTime.ElapsedGameTime.TotalSeconds, true, camera);
            }
            */
        }

        public void Draw(GameTime gameTime)
        {
            var scene = ServiceManager.GetService<SceneManager>().ActiveGameScene;
            DrawScene(gameTime, scene, scene.FirstCamera);

            Device.Clear(ClearColor);

            var rt = ServiceManager.GetService<RenderTargetService>();
            var sb = ServiceManager.GetService<SpriteBatchService>();
            
            
            sb.Batch.Begin();
            sb.Batch.Draw(rt.RawScene,
                new Rectangle(0,0, rt.RawScene.Width, rt.RawScene.Height),
                Color.White);

            sb.Batch.End();
        }

        private void DrawItemAndChildren(IEntity item, float elapsed, bool debug, ICamera camera)
        {
            var cam = camera;
            if (!debug)
            {
                IRenderable drawable = item as IRenderable;
                if (drawable != null && drawable.Visible)
                {
                    drawable.Draw(elapsed);
                }
            }
            else
            {
                IDebugRenderable debudrawable = item as IDebugRenderable;

                if (debudrawable != null)
                    debudrawable.DrawDebug(elapsed);
            }

            if (item.Children.Count > 0)
            {
                foreach (var child in item.Children)
                {
                    DrawItemAndChildren(child, elapsed, debug, cam);
                }
            }
        }
    }
}
